#include "prision.h"

#include "config.h"
#include "individual.h"

#include <deque>
#include <cstdlib>


//------------------------------------------------------------------------------
// Public functions

Prision::Prision(int p_rewards[4])
{
    m_rewards[S] = p_rewards[S];
    m_rewards[D] = p_rewards[D];
    m_rewards[C] = p_rewards[C];
    m_rewards[T] = p_rewards[T];
}

unsigned int Prision::pit(unsigned int p_gladiator_number,
    std::vector<Individual *> &p_gladiators) 
{
    unsigned int gladiators_count;
    unsigned int total_earned_reward = 0;
    Individual *gladiator;
    Individual *challenger;

    gladiator = p_gladiators.at(p_gladiator_number);

    // Test against all possible challengers.
    gladiators_count = p_gladiators.size();
    for(unsigned int i = 0; i < gladiators_count; ++i) {
        if(p_gladiator_number == i) {
            // Skip me
            continue;
        }
        // Find the next challenger.
        challenger = p_gladiators.at(i);
        // Play against him.
        total_earned_reward += play(gladiator, challenger);
    }
    
    return total_earned_reward;
}

unsigned int Prision::play(Individual *player_a, Individual *player_b)
{
    std::deque<PLAYS> player_a_history;
    std::deque<PLAYS> player_b_history;
    unsigned int max_history;
    PLAYS player_a_choise;
    PLAYS player_b_choise;
    unsigned int earnings = 0;

    // Find out how many initial games we need in the history.
    max_history = player_a->getHistorySize();
    if(max_history < player_b->getHistorySize()) {
        max_history = player_b->getHistorySize();
    }

    // Init the game history with random games.
    for(unsigned int i = 0; i < max_history; ++i) {
        player_a_history.push_front(
            static_cast<PLAYS>(std::rand() % TOTAL_PLAYS));
        player_b_history.push_front(
            static_cast<PLAYS>(std::rand() % TOTAL_PLAYS));
    }

    // Play the game a predefined amount of times.
    for(int i = 0; i < PRISION_GAMES; ++i) {
        player_a_choise = selectPlay(player_a, player_b_history);
        player_b_choise = selectPlay(player_b, player_a_history);

        // Check what was earned by each player.
        if(player_a_choise == COOPERATE && player_b_choise == COOPERATE) {
            earnings += m_rewards[Prision::C];
        }
        else if(player_a_choise == COOPERATE && player_b_choise == DEFECT) {
            earnings += m_rewards[Prision::S];
        }
        else if(player_a_choise == DEFECT && player_b_choise == COOPERATE) {
            earnings += m_rewards[Prision::T];
        }
        else if(player_a_choise == DEFECT && player_b_choise == DEFECT) {
            earnings += m_rewards[Prision::D];
        }

        // Store the played games in each player histories.
        player_a_history.push_front(player_a_choise);
        player_b_history.push_front(player_b_choise);
    }

    return earnings;
}


//------------------------------------------------------------------------------
// Private Functions

Prision::PLAYS Prision::selectPlay(Individual *p_player, 
    std::deque<PLAYS> &p_opponent_history)
{
    int multiplier = 1;
    unsigned int strategy_index = 0;
    unsigned int history_size = p_player->getHistorySize();

    // Obtain the corresponding index into our strategy (genes) from the 
    // ... opponents history.
    for(unsigned int i = 0; i < history_size; ++i) {
        strategy_index += 
            static_cast<unsigned int>(p_opponent_history.at(i)) * multiplier;
        multiplier <<= 1; // Times 2.
    }

    return static_cast<PLAYS>(p_player->getGene(strategy_index));
}
